﻿using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Effects;
using Microsoft.Graphics.Canvas.UI.Xaml;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Storage;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Navigation;
using 纸片人.AttributeUsageFloder;
using 纸片人.Shaders;

// https://go.microsoft.com/fwlink/?LinkId=234238 上介绍了“空白页”项模板

namespace 纸片人.PageViews
{
    [LastModified("2022-5-12", "create LiquefactionShaderPage.xaml")]
    [PageType("gpu", "液化")]
    /// <summary>
    /// 可用于自身或导航至 Frame 内部的空白页。
    /// </summary>
    public sealed partial class LiquefactionShaderPage : Page
    {
        CanvasImageSource cis;

        CanvasRenderTarget renderTarget;
        byte[] shaderCodeBytes;


        List<List<LiqueInfo>> revokeList = new List<List<LiqueInfo>>();
        List<List<LiqueInfo>> advanceList = new List<List<LiqueInfo>>();

        /// <summary>
        /// 临时
        /// </summary>
        List<LiqueInfo> tempLiqueInfoList = new List<LiqueInfo>();

        double rangeSize = 50;
        Vector2 mousePoint;
        Vector2 startPoint;

        Color[] source;
        int width;
        int height;
        CanvasBitmap bitmap;

        /// <summary>
        /// 绘制位置，大小
        /// </summary>
        Rect drawRect;


        private CanvasRenderTarget renderTarget2;
        byte[] shaderCodeBytes2;

        public LiquefactionShaderPage()
        {
            this.InitializeComponent();
            LoadShadeCodeAsync();
        }

        private async void LoadShadeCodeAsync()
        {
            if (shaderCodeBytes == null) shaderCodeBytes = await ShaderType.Liquefaction.LoadAsync();
            if (shaderCodeBytes2 == null) shaderCodeBytes2 = await ShaderType.LiquefactionAbsoluteSampling.LoadAsync();
        }

        /// <summary>
        /// 选择图片
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void selectPicture_Click(object sender, RoutedEventArgs e)
        {
            bitmap = await Function.SelectImageAsync();
            source = bitmap.GetPixelColors();
            width = (int)bitmap.Size.Width;
            height = (int)bitmap.Size.Height;
            if (bitmap == null)
                return;
            renderTarget = new CanvasRenderTarget(bitmap.Device, (float)bitmap.Size.Width, (float)bitmap.Size.Height, 96f);
            using (var ds = renderTarget.CreateDrawingSession())
            {
                ds.DrawImage(bitmap);
            }

            revokeCount.Text = advanceCount.Text = "0";
            revokeList.Clear();
            advanceList.Clear();

            renderTarget2 = new CanvasRenderTarget(bitmap.Device, (float)bitmap.Size.Width, (float)bitmap.Size.Height, 96f);
            using (var ds2 = renderTarget2.CreateDrawingSession())
            {
                ds2.DrawImage(bitmap);
            }

            canvas.Invalidate();
            //canvas2.Invalidate();
        }

        #region 相对位置采样
        /// <summary>
        /// 重设图片
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void reset_Click(object sender, RoutedEventArgs e)
        {
            if (bitmap != null)
            {
                renderTarget = new CanvasRenderTarget(bitmap.Device, (float)bitmap.Size.Width, (float)bitmap.Size.Height, 96f);
                using (var ds = renderTarget.CreateDrawingSession())
                {
                    ds.DrawImage(bitmap);
                }
                revokeCount.Text = advanceCount.Text = "0";
                revokeList.Clear();
                advanceList.Clear();
                renderTarget2 = new CanvasRenderTarget(bitmap.Device, (float)bitmap.Size.Width, (float)bitmap.Size.Height, 96f);
                using (var ds2 = renderTarget2.CreateDrawingSession())
                {
                    ds2.DrawImage(bitmap);
                }
                canvas.Invalidate();
                //canvas2.Invalidate();
            }


        }

        private void canvas_Draw(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
        {
            if (shaderCodeBytes == null || renderTarget == null)
                return;

          
            using (var ds = args.DrawingSession)
            {
                ds.DrawImage(renderTarget);
            }

        }

        private void DrawShader()
        {
            if (shaderCodeBytes == null || renderTarget == null)
            {
                return;
            }
            //计算需要改变的位置和大小
            //宽度
            float x = 0, y = 0, w = 0, h = 0;
            x = (float)(Math.Min(startPoint.X, mousePoint.X) - rangeSize / 2);
            y = (float)(Math.Min(startPoint.Y, mousePoint.Y) - rangeSize / 2);

            h = (float)(Math.Abs(startPoint.Y - mousePoint.Y) + rangeSize);
            w = (float)(Math.Abs(startPoint.X - mousePoint.X) + rangeSize);

            if (x > width || y > height || x < 0 || y < 0)
                return;

            drawRect.X = x;
            drawRect.Y = y;
            drawRect.Width = w;
            drawRect.Height = h;
            drawRect.Intersect(new Rect(0, 0, width, height));

            x = (float)drawRect.X;
            y = (float)drawRect.Y;
            w = (float)drawRect.Width;
            h = (float)drawRect.Height;

            //重新计算位置和偏移
            float r = (float)(rangeSize / 2 / Math.Min(w, h));
            var sp = new Vector2((float)((startPoint.X - x) / w), (float)((startPoint.Y - y) / h));
            var ep = new Vector2((float)((mousePoint.X - x) / w), (float)((mousePoint.Y - y) / h));
            var pre = (float)(pressure.Value / 100f);
            LiqueInfo info = new LiqueInfo(r, sp, ep, pre, new Rect(x, y, w, h));
            tempLiqueInfoList.Add(info);
            DrawLique(info);
        }

        void DrawLique(LiqueInfo info)
        {
            var tr = new CanvasRenderTarget(CanvasDevice.GetSharedDevice(), (float)info.Rect.Width, (float)info.Rect.Height, 96);

            using (var ds = tr.CreateDrawingSession())
            {
               
                //采样显示在画面上的指定区域
                ds.DrawImage(renderTarget, 0, 0, info.Rect);
            }
            PixelShaderEffect pse = new PixelShaderEffect(shaderCodeBytes)
            {
                Source1BorderMode = EffectBorderMode.Hard,
                Source1 = tr,
                Properties =
                {
                    ["radius"] = info.Radius,
                    ["position"] = info.StartPos,
                    ["targetPosition"] = info.EndPos,
                    ["pressure"] = (float)(pressure.Value / 100f),
                }
            };
            //将液化好的指定区域覆盖指定位置
            using (var ds = renderTarget.CreateDrawingSession())
            {
                ds.DrawImage(pse, (float)info.Rect.X, (float)info.Rect.Y, new Rect(0, 0, info.Rect.Width, info.Rect.Height));
            }
        }


        private void DrawGeneral()
        {

            var tempColors = new Color[source.Length];
            Array.Copy(source, tempColors, source.Length);
            Vector2 dPos = mousePoint - startPoint;

            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    var cpos = new Vector2(x, y);
                    // 计算当前点到目标点距离
                    var dist = Vector2.Distance(mousePoint, cpos);
                    if (rangeSize / 2 >= dist)
                    {
                        float scaleFactor = (float)(1f - dist / (rangeSize / 2));
                        scaleFactor = (50f / 100f) * scaleFactor;
                        Vector2 pos = cpos - Vector2.Multiply(scaleFactor, dPos);
                        pos.X = (int)Math.Round(pos.X);
                        pos.Y = (int)Math.Round(pos.Y);
                        var index = (int)pos.X + (int)pos.Y * width;
                        if (index >= source.Length)
                        {
                            continue;
                        }
                        var col = source[index];
                        //Debug.WriteLine(index);
                        tempColors[x + y * width] = col;
                    }
                }
            }
            source = tempColors;
            var bitmap = CanvasRenderTarget.CreateFromColors(CanvasDevice.GetSharedDevice(), source, width, height);
            using (var ds = renderTarget.CreateDrawingSession())
            {
                ds.DrawImage(bitmap);
            }
        }
        private void slider_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
        {
            rangeSize = (int)slider.Value;
            SetEllipsePosition();
        }

        /// <summary>
        /// 拖拽中
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void canvas_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
        {
            var pos = e.Position;

            mousePoint.X = (float)(startPoint.X + e.Delta.Translation.X);
            mousePoint.Y = (float)(startPoint.Y + e.Delta.Translation.Y);
            Debug.WriteLine($"sx:{startPoint.X} sy:{startPoint.Y}, mx:{mousePoint.X} my:{mousePoint.Y}  px:{pos.X} py:{pos.Y} ,ox:{e.Delta.Translation.X} oy{e.Delta.Translation.Y}");
            DrawShader();

            //DrawGeneral();
            //DrawShader2();
            startPoint = new Vector2(mousePoint.X, mousePoint.Y);
            canvas.Invalidate();
            //canvas2.Invalidate();
        }

        /// <summary>
        /// 拖拽结束
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void canvas_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
        {


            //添加到撤销列表
            revokeList.Add(tempLiqueInfoList);
            tempLiqueInfoList = new List<LiqueInfo>();

            revokeCount.Text = revokeList.Count.ToString();
            advanceCount.Text = "0";
            advanceList.Clear();
        }

        /// <summary>
        /// 鼠标在画布上移动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void canvas_PointerMoved(object sender, PointerRoutedEventArgs e)
        {
            mousePoint = e.GetCurrentPoint(canvas).Position.ToVector2();
            SetEllipsePosition();
        }

        private void SetEllipsePosition()
        {
            ell.Height = ell.Width = rangeSize;
            float x = (float)(mousePoint.X - ell.Width / 2);
            float y = (float)(mousePoint.Y - ell.Height / 2);
            ell.Translation = new Vector3(x, y, 0);
        }

        private void canvas_PointerEntered(object sender, PointerRoutedEventArgs e)
        {
            ell.Visibility = Visibility.Visible;
        }

        /// <summary>
        /// 鼠标离开画布
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void canvas_PointerExited(object sender, PointerRoutedEventArgs e)
        {
            ell.Visibility = Visibility.Collapsed;
        }

        private void canvas_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
        {
            startPoint = e.Position.ToVector2();
        }

        #endregion

        #region 绝对位置采样
        private void DrawShader2()
        {
            if (shaderCodeBytes2 == null || renderTarget2 == null)
            {
                return;
            }

            PixelShaderEffect pse = new PixelShaderEffect(shaderCodeBytes2);
            pse.Source1BorderMode = EffectBorderMode.Hard;
            pse.Source1 = renderTarget2;
            pse.Properties["radius"] = (float)(rangeSize / 2f);
            pse.Properties["position"] = startPoint;
            pse.Properties["targetPosition"] = mousePoint;
            pse.Properties["pressure"] = (float)(pressure.Value / 100f);
            var tempRenderTarget = new CanvasRenderTarget(renderTarget2.Device, (float)renderTarget2.Size.Width, (float)renderTarget2.Size.Height, 96f);
            using (var ds = tempRenderTarget.CreateDrawingSession())
            {
                ds.DrawImage(pse);
            }


            renderTarget2 = tempRenderTarget;
        }
        #endregion

        private void canvas2_Draw(CanvasControl sender, CanvasDrawEventArgs args)
        {
            if (shaderCodeBytes2 == null || renderTarget2 == null)
                return;

            using (var ds = args.DrawingSession)
            {
                ds.DrawImage(renderTarget2);
            }
        }

        /// <summary>
        /// 前进
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void advance_Click(object sender, RoutedEventArgs e)
        {
            if (advanceList.Count < 1)
                return;
            var info = advanceList.Last();
            advanceList.RemoveAt(advanceList.Count - 1);

            foreach (var item in info)
            {
                DrawLique(item);
            }


            revokeList.Add(info);
            revokeCount.Text = revokeList.Count.ToString();
            advanceCount.Text = advanceList.Count.ToString();
            canvas.Invalidate();
        }


        /// <summary>
        /// 撤销
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void revoke_Click(object sender, RoutedEventArgs e)
        {
            revokeCount.Text = revokeList.Count.ToString();
            using (var ds = renderTarget.CreateDrawingSession())
            {
                ds.Clear(Colors.Transparent);
                ds.DrawImage(bitmap);
            }

            if (revokeList.Count < 1)
                return;

            foreach (var item in revokeList)
            {
                if (item == revokeList.Last())
                    continue;
                foreach (var temp in item)
                {
                    DrawLique(temp);
                }
            }
            advanceList.Add(revokeList.Last());
            revokeList.RemoveAt(revokeList.Count - 1);
            advanceCount.Text = advanceList.Count.ToString();
            revokeCount.Text = revokeList.Count.ToString();
            canvas.Invalidate();

        }


    }


    /// <summary>
    /// 液化信息
    /// </summary>
    public struct LiqueInfo
    {
        public float Radius { get; set; }
        public Vector2 StartPos { get; set; }
        public Vector2 EndPos { get; set; }
        public float Pressure { get; set; }

        public Rect Rect { get; set; }

        public LiqueInfo(float r, Vector2 sp, Vector2 ep, float p, Rect rect)
        {
            Radius = r;
            StartPos = sp;
            EndPos = ep;
            Pressure = p;
            Rect = rect;
        }

    }

}
